#include "linked_list.h"

/* Adds new at the head of l. Returns SUCCESS in case of success, OUT_OF_MEMORY otherwise. */
int32_t linked_list_add(list* l, void* new){
  list new_l = malloc(sizeof(link), SYSTEM_ID);
  if(new_l == NULL)
    return OUT_OF_MEMORY;
  new_l->data = new;
  new_l->next = *l;
  *l = new_l;
  return SUCCESS;
}

/* Adds new at the end of list. Returns SUCCESS or OUT_OF_MEMORY */
int32_t linked_list_add_tail(list* l, void* new){
  list current, prev = NULL;
  for(current = *l; current != NULL; prev = current, current = current->next);
  return linked_list_add_after_prev(l, prev, new);
}

/* Adds new after prev. Returns SUCCESS in case of success, OUT_OF_MEMORY otherwise */
int32_t linked_list_add_after_prev(list* l, list prev, void* new){
  list new_l;
  if(prev == NULL)
    //we have to insert at the head of the list
    return linked_list_add(l, new);
  new_l = malloc(sizeof(link), SYSTEM_ID);
  if(new_l == NULL)
    return OUT_OF_MEMORY;
  new_l->data = new;
  new_l->next = prev->next;
  prev->next = new_l;
  return SUCCESS;
}

/* Deletes target from l. Uses the function compare to know whether the correct element has been found. compare must take two pointers as arguments and returns a boolean result. It also uses the function free_data to know how to free a link's payload. free_data takes one pointer as parameter and returns a boolean result, indicating if the free worked or not. Returns SUCCESS in case of success, FAILURE otherwise. */
int32_t linked_list_delete(list* l, void* target, int32_t (*compare)(void*, void*), int32_t (*free_data)(void*)){
  list current = *l, prev = NULL ;
  while(current != NULL){
    if(compare(target, current->data)){
      //we found the link to delete
      return linked_list_delete_after_prev(l, prev, current, free_data);
    }
    prev = current;
    current = current->next;
  }
  //not found
  return FAILURE;
}

/* deletes target from l. It also uses the function free_data to know how to free a link's payload. free_data takes one pointer as parameter and returns a boolean result, indicating if the free worked or not. Returns SUCCESS in case of success, FAILURE otherwise.*/
int32_t linked_list_delete_after_prev(list* l, list prev, list target,int32_t (*free_data)(void*)){
  list to_free;
  if(prev == NULL){
    //it is the first link of the list
    to_free = *l;
    *l = (*l)->next;
  }else{
    to_free = target;
    prev->next = target->next;
  }
  if(free_data != NULL){
    //the payload has to be freed
    if(!free_data(to_free->data))
      //free_data failed
      return FAILURE;
  }
  return free(to_free, SYSTEM_ID);
}

int32_t count_list(list l){
  if(l == NULL)
    return 0;
  return 1 + count_list(l->next);
}
